#ifndef __rpc_call_bkjl6sdoi7fjwe90e3wf9__
#define __rpc_call_bkjl6sdoi7fjwe90e3wf9__

#include <cstdio>
#include <exception>
#include <string>
#include <set>
#include <map>
#include <list>
#include <tuple>
#include <functional>
#include <future>
#include <memory>
#include <mutex>
#include <type_traits>
#include <random>
#include <cmath>
#include <utility>

#include <hude/base/optional.hpp>
#include "hude/utils/json.hpp"

// #include <hude/base/log.hpp>

namespace hude
{
    namespace rpc
    {
        /**********************************************************************************************
         * @brief optional_t
         */
        template< typename _value >
        using optional_t = hude::utils::optional_t< _value >;

        namespace _ns_temp
        {
            using json_t = hude::utils::json_t;

            struct arg_null_t {};

            // using rid_t = uint32_t;
            using rid_t = std::string;

            /**********************************************************************************************
             * @brief _error_item_t
             */
            using _error_item_t = std::tuple< int, const char*, const char* >;

            /**********************************************************************************************
             * @brief _error_table
             */
            static const _error_item_t    _error_table[] =
            {
            { -32700, "Parse error", "Invalid JSON was received by the server.  An error occurred on the server while parsing the JSON text." },
            { -32600, "Invalid Request", "The JSON sent is not a valid Request object." },
            { -32601, "Method not found", "The method does not exist / is not available." },
            { -32602, "Invalid params", "Invalid method parameter(s)." },
            { -32603, "Internal error", "Internal JSON-RPC error." },
            };

            /**********************************************************************************************
             * @brief _error_t
             */
            enum _error_t
            {
                emERR_PARSE,
                emERR_REQUEST,
                emERR_NOT_FOUND,
                emERR_PARAMS,
                emERR_INTERNAL,

                emERR_MAX
            };

            /**********************************************************************************************
             * @brief _callback_t
             */
            struct _callback_t
            {
                enum type_t { emAUTO, emMENUAL };

                typedef std::shared_ptr< _callback_t > ptr_callback_t;

                virtual rid_t id() const = 0;
                virtual void id( rid_t ) = 0;
                virtual int handle( const json_t&, json_t* ) = 0;
                virtual ptr_callback_t clone() const = 0;
                virtual type_t type() const = 0;
                virtual void type( type_t type ) = 0;

                virtual ~_callback_t() {}
            };

            /**********************************************************************************************
             * @brief
             */
            using _cb_result_t = std::function< void( const json_t& ) >;

            using map_callback_t = std::map< rid_t, _callback_t::ptr_callback_t >;

            using item_session_t = std::tuple< json_t, std::list< map_callback_t::iterator >, json_t::array_t, _cb_result_t >;
            enum { emREQUST, emCALLBACK, emARGS, emRESULT };

            using ptr_session_t = std::shared_ptr< item_session_t >;

            /**********************************************************************************************
             * @brief _rpc_id_t
             */
            template< typename _value >
            struct _rpc_id_t
            {
                typedef _value id_t;

                static id_t id_;

                static id_t _id() { return id_++; }
                static id_t id() { return _id(); }

                static id_t random()
                {
                    std::random_device r;

                    std::default_random_engine e1(r());
                    std::uniform_int_distribution<int> uniform_dist(1, (id_t)-1);
                    return uniform_dist(e1);
                }
            };

            template< typename _value >
            typename _rpc_id_t< _value >::id_t _rpc_id_t< _value >::id_ = _rpc_id_t< _value >::random(); //typename _rpc_id_t< _value >::id_t{};

            /**********************************************************************************************
             * @brief rpc_id_t
             */
            template< typename _value >
            struct rpc_id_t
            {
                typedef _rpc_id_t< _value > raw_t;
                typedef typename _rpc_id_t< _value >::id_t id_t;

                static id_t id() { return raw_t::id(); }
            };

            template<>
            struct rpc_id_t< std::string >
            {
                typedef _rpc_id_t< uint32_t > raw_t;
                typedef std::string id_t;

                static id_t id()
                {
                    size_t siz = sizeof( uint32_t ) * 2 + 1;

                    id_t ret;

                    ret.resize( siz );

                    siz = snprintf( &( ret[0] ), siz, "%x", raw_t::id() );

                    ret.resize( siz );

                    return ret;
                }
            };

            /**********************************************************************************************
             * @brief _rpc_context_t
             */
            template< typename _channel >
            class _rpc_context_t
            {
            public:
                typedef _rpc_context_t< _channel > this_t;

                typedef std::shared_ptr< _channel > ptr_channel_t;
                typedef std::map< rid_t, ptr_session_t > sessions_t;

                typedef std::lock_guard< std::mutex > autolock_t;

                typedef std::shared_ptr< this_t > ptr_this_t;

            public:
                /***************************************
                 * @brief _rpc_context_t::_rpc_args_set_t
                 */
                _rpc_context_t( ptr_channel_t channel ) :
                    channel_( channel )
                {
                }

                /***************************************
                 * @brief _rpc_context_t::~_rpc_args_set_t
                 */
                virtual ~_rpc_context_t()
                {
                    log_war("_rpc_context_t::~_rpc_context_t()");
                }

            public:
                /***************************************
                 * @brief _rpc_context_t::commit
                 */
                optional_t< rid_t > commit( rid_t id, ptr_session_t session, _cb_result_t result )
                {
                    autolock_t _( mutex_ );

                    auto& req = std::get< emREQUST >( *session );

                    std::get< emRESULT >( *session ) = result;

                    req["jsonrpc"] = "2.0";
                    req["id"] = id;
                    req["params"] = std::get< emARGS >( *session );

                    auto ret = sessions_.emplace( id, session );
                    if( !ret.second ) return optional_t< rid_t >{};

                    return on_send( req ) ? optional_t< rid_t >{ id } : optional_t< rid_t >{};
                }

                /***************************************
                 * @brief _rpc_context_t::release
                 */
                void release( rid_t id, bool force = false )
                {
                    autolock_t _( mutex_ );

                    auto itr = sessions_.find( id );

                    if( itr == sessions_.end() )
                    {
                        std::abort();
                    } else {
                        _release( itr, force );
                    }
                }

                /***************************************
                 * @brief _rpc_context_t::rpc_add
                 */
                optional_t< rid_t > rpc_add( const _callback_t& cb )
                {
                    autolock_t _( mutex_ );

                    auto ret = map_callback_.emplace( cb.id(), cb.clone() );

                    return ret.second ? optional_t< rid_t >{ cb.id() } : optional_t< rid_t >{};
                }

                /***************************************
                 * @brief _rpc_context_t::rpc_del
                 */
                void rpc_del( rid_t id )
                {
                    autolock_t _( mutex_ );

                    map_callback_.erase( id );
                }

                void rpc_del( const _callback_t& cb )
                {
                    rpc_del( cb.id() );
                }

            public:
                /***************************************
                 * @brief rpc_context_t::begin
                 */
                static ptr_session_t begin( const std::string& name )
                {
                    auto ptr = new item_session_t{
                        json_t{
                            { "jsonrpc", "2.0" },
                            { "id", uintptr_t{} },
                            { "method", name }
                        },
                        std::list< map_callback_t::iterator >{},
                        json_t::array_t{},
                        _cb_result_t{}
                    };

                    return ptr_session_t{ ptr };
                }

                /***************************************
                 * @brief rpc_context_t::push
                 */
                bool push( ptr_session_t session, const _callback_t& cb )
                {
                    autolock_t _( mutex_ );

                    auto ret = map_callback_.emplace( cb.id(), cb.clone() );
                    std::get< emCALLBACK >( *session ).push_back( ret.first );

                    // log_dug( "[ rpc ] push callback: %s", cb.id().c_str() );

                    return _push( session, ret.first );
                }

                template< typename _value >
                bool push( ptr_session_t session, _value value )
                {
                    autolock_t _( mutex_ );

                    return _push( session, value );
                }

                bool res_error( const json_t& req, _error_t err )
                {
                    const auto& item = _error_table[err];

                    auto res = json_t
                    {
                        { "jsonrpc", "2.0" },
                        { "id", req["id"] },
                        { "error",
                            {
                                { "code", std::get<0>(item) },
                                { "message", std::get<1>(item) },
                                { "data", std::get<2>(item) }
                            }
                        }
                    };

                    return on_send( res );
                }

                ptr_this_t get_ptr() const { return thiz_; }
                void set_ptr( ptr_this_t thiz ) { thiz_ = thiz; }

            public:
                void on_recv( const std::string& buffer )
                {
                    handle( buffer );
                }

            private:
                template< typename _value >
                static bool _push( ptr_session_t session, _value value );

            private:
                bool on_send( const json_t& json )
                {
                    if( !channel_ ) return false;

                    auto buffer = json.dump();

                    return buffer.size() == (size_t)channel_->send( buffer );
                }

                void _release( sessions_t::iterator itr, bool force )
                {
                    if( itr == sessions_.end() ) return;

                    auto cbs = std::get< emCALLBACK >( *itr->second );

                    for( auto& cb : cbs )
                    {
                        if( cb->second->type() == _callback_t::emAUTO || force )
                        {
                            // log_dug( "[ rpc ] release callback: %s", cb->second->id().c_str() );
                            map_callback_.erase( cb );
                        }
                    }

                    sessions_.erase( itr );
                }

                void handle_result( const json_t& pack )
                {
                    auto id = pack.find( "id" );

                    if( id == pack.end() ) { return; }

                    ptr_session_t ptr;

                    {
                        autolock_t _( mutex_ );

                        auto itr = sessions_.find( id->get< rid_t >() );

                        if( itr == sessions_.end() )
                            return;

                        ptr = itr->second;
                    }

                    std::get< emRESULT >( *ptr )( pack );
                }

                void handle_request( const json_t& pack )
                {
                    auto m = pack["method"].get< rid_t >();

                    _callback_t::ptr_callback_t cb;

                    {
                        autolock_t _( mutex_ );

                        auto itr = map_callback_.find( m );

                        if( itr == map_callback_.end() )
                        {
                            res_error( pack, emERR_NOT_FOUND );
                            return;
                        }

                        cb = itr->second;
                    }

                    json_t res;

                    auto ret = cb->handle( pack, &res );

                    if( pack.find( "id" ) == pack.end() ) return;

                    if( ret )
                    {
                        res_error( pack, emERR_INTERNAL );

                        return;
                    }

                    on_send( res );
                }

                void handle( const std::string& buffer )
                {
                    try {
                        auto pack = json_t::parse( buffer );

                        auto rpc = pack.find( "jsonrpc" );

                        if( pack.empty() || rpc == pack.end() || rpc->get< std::string >() != "2.0" )
                        {
                            res_error( nullptr, emERR_REQUEST );
                            return;
                        }

                        auto m = pack.find( "method" );

                        if( m == pack.end() )
                        {
                            handle_result( pack );
                        } else {
                            handle_request( pack );
                        }
                    } catch( const std::exception& ) {
                        res_error( nullptr, emERR_PARSE );
                    }
                }

            private:
                ptr_channel_t channel_;
                std::mutex mutex_;
                map_callback_t map_callback_;
                sessions_t sessions_;
                ptr_this_t thiz_;

            private:
                _rpc_context_t() = delete;
                _rpc_context_t& operator=( const _rpc_context_t& ) = delete;
            };

            /**********************************************************************************************
             * @brief _base_callback_t
             */
            template< typename _channel >
            struct _base_callback_t : public _callback_t
            {
                typedef typename _rpc_context_t< _channel >::ptr_this_t context_t;

                context_t ctx_;
                type_t type_;
                rid_t id_;

                _base_callback_t() : type_( emAUTO ), id_( rpc_id_t< rid_t >::id() ) {}
                _base_callback_t( type_t type, rid_t id = rpc_id_t< rid_t >::id() ) : type_( type ), id_( id ) {}
                _base_callback_t( context_t ctx, type_t type, rid_t id = rpc_id_t< rid_t >::id() ) : ctx_( ctx ), type_( type ), id_( id ) {}

                context_t ctx() const { return ctx_; }
                void ctx( context_t c ) { ctx_ = c; }

                rid_t id() const override { return id_; }
                void id( rid_t id ) override { id_ = id; }

                type_t type() const override { return type_; }
                void type( type_t type ) override { type_ = type; }

                void context( context_t ctx ) { ctx_ = ctx; }
                context_t context() const { return ctx_; }

                optional_t< rid_t > add_to( context_t ctx ) { ctx_ = ctx; return ctx->rpc_add( *this ); }
                void del_from( context_t ctx ) { ctx->rpc_del( id() ); }
            };

            /**********************************************************************************************
             * @brief _rpc_meta_type_t
             */
            template< typename _value, typename _target_true = _callback_t, typename _target_false = _value >
            struct _rpc_meta_type_t
            {
                using real_t = typename std::remove_reference< typename std::remove_cv< _value >::type >::type;
                using type_t = typename std::conditional< std::is_base_of< _callback_t, real_t >::value, _target_true, _target_false >::type;
            };

            /**********************************************************************************************
             * @brief rpc_is_t
             */
            template< typename _value > struct _rpc_is_t;

            template<> struct _rpc_is_t< int8_t       > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "n" && arg["v"].is_number_integer();  } };
            template<> struct _rpc_is_t< int16_t      > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "n" && arg["v"].is_number_integer();  } };
            template<> struct _rpc_is_t< int32_t      > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "n" && arg["v"].is_number_integer();  } };
            template<> struct _rpc_is_t< int64_t      > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "n" && arg["v"].is_number_integer();  } };

            template<> struct _rpc_is_t< uint8_t      > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "n" && arg["v"].is_number_unsigned(); } };
            template<> struct _rpc_is_t< uint16_t     > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "n" && arg["v"].is_number_unsigned(); } };
            template<> struct _rpc_is_t< uint32_t     > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "n" && arg["v"].is_number_unsigned(); } };
            template<> struct _rpc_is_t< uint64_t     > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "n" && arg["v"].is_number_unsigned(); } };

            template<> struct _rpc_is_t< float        > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "n" && arg["v"].is_number_float();    } };
            template<> struct _rpc_is_t< double       > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "n" && arg["v"].is_number_float();    } };
            template<> struct _rpc_is_t< bool         > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "b" && arg["v"].is_boolean();         } };

            template<> struct _rpc_is_t< std::string  > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "s" && arg["v"].is_string();          } };

            template<> struct _rpc_is_t< json_t       > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "t" && arg["v"].is_object();          } };

            template<> struct _rpc_is_t< arg_null_t   > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "z";                             } };
            template<> struct _rpc_is_t< _callback_t  > { static bool is( const json_t& arg ) { return arg["t"].get< std::string >() == "f";                             } };

            template< typename _value > struct rpc_is_t
            {
                static bool is( const json_t::array_t& args, size_t n ) {
                    return _rpc_is_t< typename _rpc_meta_type_t< _value >::type_t >::is( args[n] ); }
            };

            /**********************************************************************************************
             * @brief rpc_fromjson_t
             */
            template< typename _value > struct _rpc_fromjson_t {};

            template<> struct _rpc_fromjson_t< int8_t      > { static void pull( const json_t& arg, int8_t &     v ) { v = arg["v"].get< int8_t      >(); } };
            template<> struct _rpc_fromjson_t< int16_t     > { static void pull( const json_t& arg, int16_t&     v ) { v = arg["v"].get< int16_t     >(); } };
            template<> struct _rpc_fromjson_t< int32_t     > { static void pull( const json_t& arg, int32_t&     v ) { v = arg["v"].get< int32_t     >(); } };
            template<> struct _rpc_fromjson_t< int64_t     > { static void pull( const json_t& arg, int64_t&     v ) { v = arg["v"].get< int64_t     >(); } };

            template<> struct _rpc_fromjson_t< uint8_t     > { static void pull( const json_t& arg, uint8_t&     v ) { v = arg["v"].get< uint8_t     >(); } };
            template<> struct _rpc_fromjson_t< uint16_t    > { static void pull( const json_t& arg, uint16_t&    v ) { v = arg["v"].get< uint16_t    >(); } };
            template<> struct _rpc_fromjson_t< uint32_t    > { static void pull( const json_t& arg, uint32_t&    v ) { v = arg["v"].get< uint32_t    >(); } };
            template<> struct _rpc_fromjson_t< uint64_t    > { static void pull( const json_t& arg, uint64_t&    v ) { v = arg["v"].get< uint64_t    >(); } };

            template<> struct _rpc_fromjson_t< float       > { static void pull( const json_t& arg, float&       v ) { v = arg["v"].get< float       >(); } };
            template<> struct _rpc_fromjson_t< double      > { static void pull( const json_t& arg, double&      v ) { v = arg["v"].get< double      >(); } };
            template<> struct _rpc_fromjson_t< bool        > { static void pull( const json_t& arg, bool&        v ) { v = arg["v"].get< bool        >(); } };

            template<> struct _rpc_fromjson_t< std::string > { static void pull( const json_t& arg, std::string& v ) { v = arg["v"].get< std::string >(); } };

            template<> struct _rpc_fromjson_t< json_t      > { static void pull( const json_t& arg, json_t&      v ) { v = arg["v"].get< json_t      >(); } };

            template<> struct _rpc_fromjson_t< arg_null_t  > { static void pull( const json_t&    , arg_null_t&    ) {                                    } };

            template<> struct _rpc_fromjson_t< _callback_t >
            {
                static void pull( const json_t& arg, _callback_t& v )
                {
                    v.id( arg["v"].get< std::string >() );
                }
            };

            template< typename _value > struct rpc_fromjson_t
            {
                static void pull( const json_t::array_t& args, size_t n, _value& v ) {
                    _rpc_fromjson_t< typename _rpc_meta_type_t< _value >::type_t >::pull( args[n], v ); }
            };

            /**********************************************************************************************
             * @brief _set_n
             */
            inline void _set_n( json_t& arg ) { arg = json_t( { { "t", "z" } } ); }

            /**********************************************************************************************
             * @brief _set_v
             */
            template< typename _value >
            inline void _set_v( json_t& arg, const char* t, _value v ) { arg = json_t( { { "t", t }, { "v", v } } ); }

            template<>
            inline void _set_v< const char* >( json_t& arg, const char* t, const char* v ) { arg = json_t( { { "t", t }, { "v", std::string { v } } } ); }

            /**********************************************************************************************
             * @brief rpc_tojson_t
             */

            template< typename _value > struct _rpc_tojson_t;

            template<> struct _rpc_tojson_t< int8_t      > { static void set( json_t& arg, int8_t             n ) { _set_v( arg, "n", n ); } };
            template<> struct _rpc_tojson_t< int16_t     > { static void set( json_t& arg, int16_t            n ) { _set_v( arg, "n", n ); } };
            template<> struct _rpc_tojson_t< int32_t     > { static void set( json_t& arg, int32_t            n ) { _set_v( arg, "n", n ); } };
            template<> struct _rpc_tojson_t< int64_t     > { static void set( json_t& arg, int64_t            n ) { _set_v( arg, "n", n ); } };

            template<> struct _rpc_tojson_t< uint8_t     > { static void set( json_t& arg, uint8_t            n ) { _set_v( arg, "n", n ); } };
            template<> struct _rpc_tojson_t< uint16_t    > { static void set( json_t& arg, uint16_t           n ) { _set_v( arg, "n", n ); } };
            template<> struct _rpc_tojson_t< uint32_t    > { static void set( json_t& arg, uint32_t           n ) { _set_v( arg, "n", n ); } };
            template<> struct _rpc_tojson_t< uint64_t    > { static void set( json_t& arg, uint64_t           n ) { _set_v( arg, "n", n ); } };

            template<> struct _rpc_tojson_t< float       > { static void set( json_t& arg, float              n ) { _set_v( arg, "n", n ); } };
            template<> struct _rpc_tojson_t< double      > { static void set( json_t& arg, double             n ) { _set_v( arg, "n", n ); } };

            template<> struct _rpc_tojson_t< bool        > { static void set( json_t& arg, bool               n ) { _set_v( arg, "b", n ); } };

            template<> struct _rpc_tojson_t< std::string > { static void set( json_t& arg, const std::string& s ) { _set_v( arg, "s", s ); } };
            template<> struct _rpc_tojson_t< const char* > { static void set( json_t& arg, const char*        s ) { _set_v( arg, "s", s ); } };

            template<> struct _rpc_tojson_t< json_t      > { static void set( json_t& arg, const json_t&      j ) { _set_v( arg, "t", j ); } };

            template<> struct _rpc_tojson_t< arg_null_t  > { static void set( json_t& arg, arg_null_t           ) { _set_n( arg );         } };

            template<> struct _rpc_tojson_t< map_callback_t::iterator >
            {
                static void set( json_t& arg, map_callback_t::iterator itr ) {

                    _set_v( arg, "f", itr->first );
                }
            };

            template< typename _value > struct rpc_tojson_t
            {
                static void push( json_t::array_t& args, _value value )
                {
                    json_t item;

                    _rpc_tojson_t< _value >::set( item, value );

                    args.push_back( item );
                }
            };

            /**********************************************************************************************
             * @brief _bind()
             */
            template< typename _channel, typename _value >
            static void _bind( std::shared_ptr< _rpc_context_t< _channel > >, _value& ) {}

            template< typename _channel >
            static void _bind( std::shared_ptr< _rpc_context_t< _channel > > ctx, _base_callback_t< _channel >& cb ) { cb.context( ctx ); }

            template< typename _channel, typename _value >
            static void bind( std::shared_ptr< _rpc_context_t< _channel > > ctx, _value& value )
            {
                using type_t = typename std::conditional< std::is_base_of< _base_callback_t< _channel >, _value >::value,
                    _base_callback_t< _channel >, _value >::type;

                _bind( ctx, static_cast< type_t& >( value ) );
            }

            /**********************************************************************************************
             * @brief _remove_cvref
             */
            template <class T>
            struct _remove_cvref
            {
                typedef std::remove_cv_t< std::remove_reference_t< T > > type;
            };

            /**********************************************************************************************
             * @brief _rpc_context_t< _channel >::_push()
             */
            template< typename _channel >
            template< typename _value >
            bool _rpc_context_t< _channel >::_push( ptr_session_t session, _value value )
            {
                auto& args = std::get< emARGS >( *session );

                rpc_tojson_t< typename _remove_cvref< _value >::type >::push( args, value );

                return true;
            }

            /**********************************************************************************************
             * @brief _rpc_args_get_t
             */
            template< typename _context, int _index, typename _collect, typename ... _args > struct _rpc_args_get_t;

            template< typename _context, int _index, typename _collect, typename _first, typename ... _other >
            struct _rpc_args_get_t< _context, _index, _collect, _first, _other... >
            {
                bool get( _context ctx, const json_t::array_t& args, int offset, _collect& collect )
                {
                    if( args.size() > static_cast< size_t >( offset + _index ) )
                    {
                        if( !rpc_is_t< typename _remove_cvref< _first >::type >::is( args, offset + _index ) )
                            return false;

                        rpc_fromjson_t< typename _remove_cvref< _first >::type >::pull( args, offset + _index, std::get< _index >( collect ) );

                        bind( ctx, std::get< _index >( collect ) );

                        return _rpc_args_get_t< _context, _index + 1, _collect, _other... >().get( ctx, args, offset, collect );
                    } else {
                        return false;
                    }
                }
            };

            template< typename _context, int _index, typename _collect >
            struct _rpc_args_get_t< _context, _index, _collect >
            {
                bool get( _context, const json_t::array_t&, int, _collect& ) { return true; }
            };

            /**********************************************************************************************
             * @brief rpc_args_get_t
             */
            template< typename _context, typename ... _args >
            struct rpc_args_get_t
            {
                typedef std::tuple< typename _remove_cvref< _args >::type ... > collect_t;

                _context ctx_;
                collect_t collect_;

                operator collect_t& () { return collect_; }
                operator const collect_t& ( ) const { return collect_; }

                bool operator()( const json_t::array_t& args, int offset ) { return this->gets( args, offset ); }

                rpc_args_get_t() {}
                rpc_args_get_t( _context ctx, const json_t::array_t& args, int offset ) : ctx_( ctx ) { this->gets( args, offset ); }

                bool gets( const json_t::array_t& args, int offset )
                {
                    return _rpc_args_get_t< _context, 0, collect_t, _args ... >().get( ctx_, args, offset, collect_ );
                }
            };

            /**********************************************************************************************
             * @brief ptr_context_t
             */
            template< typename _channel >
            using ptr_context_t = std::shared_ptr< _rpc_context_t< _channel > >;

            /**********************************************************************************************
             * @brief _rpc_args_set_t
             */
            template< typename _channel, typename ... _args >
            struct _rpc_args_set_t;

            template< typename _channel, typename _first, typename ... _other >
            struct _rpc_args_set_t< _channel, _first, _other... >
            {
                void set( ptr_context_t< _channel > ctx, ptr_session_t session, _first first, _other ... other )
                {
                    ctx->push( session, std::forward< typename _rpc_meta_type_t< _first, const _callback_t& >::type_t >( first ) );

                    _rpc_args_set_t< _channel, _other... >().set( ctx, session, std::forward< _other >( other )... );
                }
            };

            template< typename _channel >
            struct _rpc_args_set_t< _channel >
            {
                void set( ptr_context_t< _channel >, ptr_session_t session ) {}
            };

            /**********************************************************************************************
             * @brief rpc_args_set_t
             */
            struct rpc_args_set_t
            {
                template< typename _channel, typename ... _args >
                static void set( ptr_context_t< _channel > ctx, ptr_session_t session, _args ... args )
                {
                    _rpc_args_set_t< _channel, _args ... >().set( ctx, session, std::forward< _args >( args )... );
                }
            };

            /**********************************************************************************************
             * @brief _async_call_t
             */
            template< typename _channel, typename _result >
            struct _async_call_t
            {
                typedef std::function< void( const json_t*, _result&& ) > cb_result_t;

                ptr_context_t< _channel > ctx_;
                std::string name_;
                std::mutex mutex_;
                std::set< rid_t > ids_;
                uintptr_t v_;

                _async_call_t( ptr_context_t< _channel > ctx, const std::string& name ) :
                    ctx_( ctx ), name_( name ), v_( 0U )
                {
                }

                ~_async_call_t()
                {
                    std::lock_guard< std::mutex > al( mutex_ );

                    for( auto& id : ids_ )
                    {
                        // log_dug( "_async_call_t::~_async_call_t() { release id = %s }", id.c_str() );

                        ctx_->release( id, true );
                    }
                }

                template< typename ... _args >
                inline optional_t< rid_t > operator()( cb_result_t cb_result, _args ... args )
                {
                    return call( std::forward< cb_result_t >( cb_result ), std::forward< _args >( args )... );
                }

                template< typename ... _args >
                optional_t< rid_t > call( cb_result_t cb_result, _args ... args )
                {
                    // assert( ( "caller repeated use.", v_ == 0U ) );
                    assert( v_ == 0U );

                    v_ = (uintptr_t)(void*)this;

                    auto _cb_result = [ this ]( uintptr_t v, rid_t id, cb_result_t cb_result, const json_t& res ) -> void
                    {
                        // assert( ( "object address error.", v == v_ ) );
                        assert( v == v_ );

                        if( cb_result )
                        {
                            auto r = res.find( "result" ); //.get< int >();

                            if( r == res.end() )
                            {
                                cb_result( &res, _result() );
                            } else {
                                if( _rpc_is_t< _result >::is( *r ) )
                                {
                                    _result result;

                                    _rpc_fromjson_t< _result >::pull( *r, result );

                                    cb_result( nullptr, std::move( result ) );
                                } else {
                                    cb_result( &res, _result{} );
                                }
                            }
                        }
                    };

                    auto session = ctx_->begin( name_ );

                    rpc_args_set_t::set( ctx_, session, std::forward< _args >( args )... );

                    auto id = rpc_id_t< rid_t >::id();

                    {
                        std::lock_guard< std::mutex > al( mutex_ );
                        ids_.insert( id );
                        // log_dug( "_async_call_t::call() { commit id = %s }", id.c_str() );

                    }

                    return ctx_->commit( id, session, std::bind( _cb_result, v_, id, cb_result, std::placeholders::_1 ) );
                }
            };

            template< typename _channel >
            struct _async_call_t< _channel, void >
            {
                typedef std::function< void( const json_t* ) > cb_result_t;

                ptr_context_t< _channel > ctx_;
                std::string name_;
                std::mutex mutex_;
                std::set< rid_t > ids_;
                uintptr_t v_;

                _async_call_t( ptr_context_t< _channel > ctx, const std::string& name ) :
                    ctx_( ctx ), name_( name ), v_( 0U )
                {
                }

                ~_async_call_t()
                {
                    std::lock_guard< std::mutex > al( mutex_ );

                    for( auto& id : ids_ )
                    {
                        // log_dug( "_async_call_t::~_async_call_t() { release id = %s }", id.c_str() );

                        ctx_->release( id, true );
                    }
                }

                template< typename ... _args >
                optional_t< rid_t > operator()( cb_result_t cb_result, _args ... args )
                {
                    return call( std::forward< cb_result_t >( cb_result ), std::forward< _args >( args )... );
                }

                template< typename ... _args >
                optional_t< rid_t > call( cb_result_t cb_result, _args ... args )
                {
                    // assert( ( "caller repeated use.", v_ == 0U ) );
                    assert( v_ == 0U );

                    v_ = (uintptr_t)(void*)this;

                    auto _cb_result = [=]( uintptr_t v, rid_t id, cb_result_t cb_result, const json_t& res ) -> void
                    {
                        // assert( ( "object address error.", v == v_ ) );
                        assert( v == v_ );

                        if( cb_result )
                        {
                            auto e = res.find( "error" ); //.get< int >();
                            cb_result( ( e == res.end() )? nullptr : &res );
                        }
                    };

                    auto session = ctx_->begin( name_ );

                    rpc_args_set_t::set( ctx_, session, std::forward< _args >( args )... );

                    auto id = rpc_id_t< rid_t >::id();

                    {
                        std::lock_guard< std::mutex > al( mutex_ );
                        ids_.insert( id );
                    }

                    return ctx_->commit( id, session, std::bind( _cb_result, v_, id, cb_result, std::placeholders::_1 ) );
                }
            };

            /**********************************************************************************************
             * @brief _sync_call_t
             */
            template< typename _channel, typename _ret >
            struct _sync_call_t
            {
                typedef optional_t< _ret > ret_t;

                ptr_context_t< _channel > ctx_;
                std::string name_;
                std::chrono::milliseconds timeout_;
                std::promise< _ret > pm_;
                _async_call_t< _channel, _ret > caller_;

                _sync_call_t( ptr_context_t< _channel > ctx, const std::string& name, std::chrono::milliseconds timeout = std::chrono::milliseconds{ 10 * 1000 } ) :
                    ctx_( ctx ), name_( name ), timeout_( timeout ), caller_( ctx_, name_ )
                {
                }

                ~_sync_call_t()
                {
                }

                template< typename ... _args >
                ret_t operator()( _args ... args )
                {
                    try {
                        auto cb_result = [=]( const json_t* e, _ret&& r ) {
                            if( e )
                                pm_.set_exception( std::current_exception() );
                            else
                                pm_.set_value( std::forward< _ret >( r ) );
                        };

                        pm_ = std::promise< _ret >{};
                        auto ft = pm_.get_future();

                        ret_t ret;
                        optional_t< rid_t > id;

                        id = caller_( cb_result, std::forward< _args >( args )... );

                        if( !id )
                        {
                            return ret_t{};
                        }

                        ret = ( ft.wait_for( timeout_ ) == std::future_status::ready )?
                            ret_t{ ft.get() } : ret_t{};

                        return ret;
                    } catch( const std::exception& ) {
                        return ret_t{};
                    }
                }
            };

            template< typename _channel >
            struct _sync_call_t< _channel, void >
            {
                ptr_context_t< _channel > ctx_;
                std::string name_;
                std::chrono::milliseconds timeout_;
                std::promise< void > pm_;
                _async_call_t< _channel, void > caller_;

                _sync_call_t( ptr_context_t< _channel > ctx, const std::string& name, std::chrono::milliseconds timeout = std::chrono::milliseconds{ 10 * 1000 } ) :
                    ctx_( ctx ), name_( name ), timeout_( timeout ), caller_( ctx_, name_ )
                {
                }

                ~_sync_call_t()
                {
                }

                template< typename ... _args >
                bool operator()( _args ... args )
                {
                    try {
                        auto cb_result = [this]( const json_t* e )
                        {
                            if( e )
                                pm_.set_exception( std::current_exception() );
                            else
                                pm_.set_value();
                        };

                        pm_ = std::promise< void >{};
                        auto ft = pm_.get_future();

                        bool ret = false;
                        optional_t< rid_t > id;

                        id = caller_( cb_result, std::forward< _args >( args )... );

                        if( !id ) return false;

                        ret = ( ft.wait_for( timeout_ ) == std::future_status::ready )?  true : false;

                        return ret;
                    } catch( const std::exception& ) {
                        return false;
                    }
                }
            };

            /**********************************************************************************************
             * @brief _rpc_invoke_t
             */
            template< typename _context, typename _ret, typename ... _args >
            struct _rpc_invoke_t
            {
                typedef std::function< _ret( _args ... ) > cb_t;

                typedef rpc_args_get_t< _context, _args ... > args_t;

                template< typename _call, typename _type, _type ..._indx >
                static _ret _invoke( _call call, typename args_t::collect_t const& collect, std::integer_sequence< _type, _indx ... > )
                {
                    return call( std::get< _indx >( collect ) ... );
                }

                static _ret invoke( _context ctx, const json_t::array_t& req, cb_t cb )
                {
                    args_t args{ ctx, req, 0 };

                    return cb ? _invoke( cb, args.collect_, std::index_sequence_for< _args ... >() ) : _ret();
                }
            };

            template< typename _context, typename ... _args >
            struct _rpc_invoke_t< _context, void, _args ... >
            {
                typedef std::function< void( _args ... ) > cb_t;

                typedef rpc_args_get_t< _context, _args ... > args_t;

                template< typename _call, typename _type, _type ... _indx >
                static void _invoke( _call call, typename args_t::collect_t const& collect, std::integer_sequence< _type, _indx ... > )
                {
                    call( std::get< _indx >( collect ) ... );
                }

                static void invoke( _context ctx, const json_t::array_t& req, cb_t cb )
                {
                    args_t args{ ctx, req, 0 };

                    if( cb ) _invoke( cb, args.collect_, std::index_sequence_for< _args ... >() );
                }
            };

            /**********************************************************************************************
             * @brief rpc_callback_t
             */
            template< typename _channel, typename ... _any > struct rpc_callback_t;

            template< typename _channel, typename _ret, typename ... _args >
            struct rpc_callback_t< _channel, _ret( _args ... ) > : public _base_callback_t< _channel >
            {
                typedef _base_callback_t< _channel > base_t;

                typedef rpc_callback_t< _channel, _ret( _args ... ) > this_t;

                typedef typename base_t::context_t   context_t;

                typedef _rpc_invoke_t< context_t, _ret, _args ... > invoke_t;

                typedef _sync_call_t< _channel, _ret > sync_call_t;

                typedef typename base_t::type_t type_t;

                typename invoke_t::cb_t cb_;

                rpc_callback_t() : base_t( context_t{}, type_t::emAUTO, rpc_id_t< rid_t >::id() ) {}

                rpc_callback_t( const typename invoke_t::cb_t& cb, type_t type = type_t::emAUTO, rid_t id = rpc_id_t< rid_t >::id() ) :
                    base_t( type, id ), cb_( cb ) {}

                rpc_callback_t( const rpc_callback_t& ins ) : base_t( ins.ctx_, ins.type_, ins.id_ ), cb_( ins.cb_ ) {}

                rpc_callback_t& operator=( const rpc_callback_t& ins )
                {
                    if( this != &ins )
                    {
                        base_t::ctx_  = ins.ctx_;
                        base_t::type_ = ins.type_;
                        base_t::id_   = ins.id_;
                        cb_           = ins.cb_;
                    }

                    return *this;
                }

                int handle( const json_t& req, json_t* res ) override
                {
                    auto ret = invoke_t::invoke( base_t::context(), req["params"].get< json_t::array_t >(), cb_ );

                    if( res )
                    {
                        json_t r;

                        _rpc_tojson_t< _ret >::set( r, ret );

                        auto itr = req.find( "id" );

                        if( itr == req.end() )
                        {
                            return -1;
                        }

                        *res = json_t::object_t{ { "jsonrpc", "2.0" }, { "result", r }, { "id", itr->get< rid_t >() } };
                    }

                    return 0;
                }

                _ret operator()( _args ... args )
                {
                    return sync_call_t{ base_t::ctx_, base_t::id_ }( args ... );
                }

                typename _callback_t::ptr_callback_t clone() const override
                {
                    return typename _callback_t::ptr_callback_t{ new this_t{ *this } };
                }
            };

            template< typename _channel, typename ... _args >
            struct rpc_callback_t< _channel, void( _args ... ) > : public _base_callback_t< _channel >
            {
                typedef _base_callback_t< _channel > base_t;

                typedef rpc_callback_t< _channel, void( _args ... ) > this_t;

                typedef typename base_t::context_t   context_t;

                typedef _rpc_invoke_t< context_t, void, _args ... > invoke_t;

                typedef _sync_call_t< _channel, void > sync_call_t;

                typedef typename base_t::type_t type_t;

                typename invoke_t::cb_t cb_;

                rpc_callback_t() {}

                rpc_callback_t( const typename invoke_t::cb_t& cb, type_t type = base_t::emAUTO, rid_t id = rpc_id_t< rid_t >::id() ) :
                    base_t( type, id ), cb_( cb ) {}

                rpc_callback_t( const rpc_callback_t& ins ) : base_t( ins.ctx_, ins.type_, ins.id_ ), cb_( ins.cb_ ) {}

                rpc_callback_t& operator=( const rpc_callback_t& ins )
                {
                    if( this != &ins )
                    {
                        base_t::ctx_  = ins.ctx_;
                        base_t::type_ = ins.type_;
                        base_t::id_   = ins.id_;
                        cb_           = ins.cb_;
                    }

                    return *this;
                }

                int handle( const json_t& req, json_t* res ) override
                {
                    invoke_t::invoke( base_t::context(), req["params"].get< json_t::array_t >(), cb_ );

                    if( res )
                    {
                        json_t r;

                        _rpc_tojson_t< arg_null_t >::set( r, arg_null_t{} );

                        auto itr = req.find( "id" );

                        if( itr == req.end() )
                        {
                            return -1;
                        }

                        *res = json_t::object_t{ { "jsonrpc", "2.0" }, { "result", r }, { "id", itr->get< rid_t >() } };
                    }

                    return 0;
                }

                void operator()( _args ... args )
                {
                    sync_call_t{ base_t::ctx_, base_t::id_ }( std::forward< _args >( args ) ... );
                }

                typename _callback_t::ptr_callback_t clone() const override
                {
                    return typename _callback_t::ptr_callback_t{ new this_t( *this ) };
                }
            };
        } //namespace _ns_temp

        /**********************************************************************************************
         * @brief rpc_t
         */
        template< typename _channel >
        struct rpc_t
        {
            using rid_t = _ns_temp::rid_t;
            using rid_gen_t = _ns_temp::rpc_id_t< rid_t >;

            using type_cb_t = typename _ns_temp::_callback_t::type_t;

            using json_t = nlohmann::json;

            using raw_channel_t = _channel;
            using channel_t = std::shared_ptr< _channel >;

            using raw_context_t = _ns_temp::_rpc_context_t< _channel >;
            using context_t = std::shared_ptr< raw_context_t >;

            template< typename _ret, typename ... _any >
            using cb_t = _ns_temp::rpc_callback_t< _channel, _ret, _any ... >;

            template< typename _ret >
            using async_call_t = _ns_temp::_async_call_t< _channel, _ret >;

            template< typename _ret >
            using sync_call_t = _ns_temp::_sync_call_t< _channel, _ret >;

            static inline context_t make_context( channel_t ch )
            {
                context_t ret( new raw_context_t( ch ) );
                ret->set_ptr( ret );
                return ret;
            }
        };
    } //namespace rpc
} //namespace hude

#endif //__rpc_call_bkjl6sdoi7fjwe90e3wf9__
//file end

